package com.dronff.ilgoto.core;

import com.dronff.ilgoto.annotation.EntryDefine;
import com.dronff.ilgoto.annotation.Goto;
import com.dronff.ilgoto.annotation.NameDefine;
import com.dronff.ilgoto.annotation.OnBefore;
import com.dronff.ilgoto.annotation.OnPost;
import com.dronff.ilgoto.interfaces.OnBeforeInterface;
import com.dronff.ilgoto.interfaces.OnPostInterface;
import com.dronff.ilgoto.test.TestClass;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @author tyf
 * 2022/4/7
 */
public class ClassILGoto extends ILGoto{
    Map<String,Method> cache4Map = null;
    Class clazz = null;
    Method entryMethod = null;
    Object object = null;
    public ClassILGoto(){
    }
    //传入需要解析goto的class和其实体对象
    public void start(Class clazz,Object object) throws Exception {
        if(clazz == null || object == null){
            throw new NullPointerException();
        }
        start_(clazz,object);
    }
    //传入需要解析goto的class和其实体对象和入口方法名
    public void start(Class clazz,Object object,String entryName) throws Exception {
        if(clazz == null || object == null){
            throw new NullPointerException();
        }
        try {
            Method tempMethod = clazz.getDeclaredMethod(entryName);
            if(tempMethod!=null){
                this.entryMethod = tempMethod;
            }
        }catch (NoSuchMethodException e){
            e.printStackTrace();
        }
        start_(clazz,object);
    }
    private void start_(Class clazz,Object obj) throws Exception {
        //缓存中的obj与传来的obj相同，直接运行
        if(cache4Map==null||this.clazz!=clazz||!this.object.equals(obj)){
            this.object = obj;
            cache4Map = new HashMap<>();
            this.clazz = clazz;
            for (Method declaredMethod : clazz.getDeclaredMethods()) {
                if(this.entryMethod==null){
                    if(declaredMethod.isAnnotationPresent(EntryDefine.class)){
                        this.entryMethod = declaredMethod;
                    }
                }
                NameDefine currentNameDefine = declaredMethod.getAnnotation(NameDefine.class);
                if(currentNameDefine!=null) {
                    //方法被命名了，存入map
                    cache4Map.put(currentNameDefine.value(), declaredMethod);
                }
            }
            if(this.entryMethod == null){
                throw new Exception("不存在入口方法");
            }
        }
        run(entryMethod);
    }
    //已经获取完所有的方法，开始运行
    private void run(Method currentMethod) throws Exception {

        onBefore(currentMethod);
        currentMethod.invoke(this.object);
        onPost(currentMethod);
        if(currentMethod.isAnnotationPresent(Goto.class)){
            run(getCandidateMethodByName(currentMethod.getAnnotation(Goto.class).value()));
        }else{
            return;
        }
    }
    private Method getCandidateMethodByName(String method) throws Exception {
        Method currentMethod = this.cache4Map.get(method);
        if(currentMethod == null){
            throw new Exception("Goto的目标不存在");
        }
        return currentMethod;
    }
    public static void main(String[] args) throws Exception {
        new ClassILGoto().start(TestClass.class,new TestClass(),"oneEntry");
    }
    private void onBefore(Method method) throws Exception {
        if(this.object instanceof OnBeforeInterface){
            ((OnBeforeInterface) this.object).onBeforeRun();
        }
        if(method.isAnnotationPresent(OnBefore.class)){
            run(getCandidateMethodByName(method.getAnnotation(OnBefore.class).value()));
        }
    }
    private void onPost(Method method) throws Exception {
        if(this.object instanceof OnPostInterface){
            ((OnPostInterface) this.object).onPostRun();
        }
        if(method.isAnnotationPresent(OnPost.class)){
            run(getCandidateMethodByName(method.getAnnotation(OnPost.class).value()));
        }
    }
}
